﻿using System;
using System.IO;
using System.Xml;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Collections.Generic;

namespace Vision.VisionWorkshop.Common
{
    /// <summary>
    /// 
    /// </summary>
    public class IntillisenseDictionary : IDisposable
    {
        private XmlDocument IntillisenseDocument { get; set; }
        
        /// <summary>
        /// 
        /// </summary>
        public IntillisenseDictionary()
        {
            this.IntillisenseDocument = new XmlDocument();
            
            XmlNode rootNode = this.IntillisenseDocument.CreateNode(XmlNodeType.Element, "IntillisenseDictionary", null);
            this.IntillisenseDocument.AppendChild(rootNode);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileData"></param>
        public void LoadDictionary(byte[] fileData)
        {
            using (MemoryStream mStream = new MemoryStream(fileData)){
                this.IntillisenseDocument.Load(mStream);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileData"></param>
        public void SaveDictionary(out byte[] fileData)
        {
            fileData = new byte[0];
            using (MemoryStream mStream = new MemoryStream()){
                this.IntillisenseDocument.Save(mStream);
                fileData = mStream.ToArray();
            }            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="assembly"></param>
        public void AddAssembly(Assembly assembly)
        {
            if (assembly != null){
                if (!this.GetAssemblyList().Contains(assembly.FullName)){
                    XmlNode assemblyNode = this.IntillisenseDocument.CreateNode(XmlNodeType.Element, "Assembly", null);
                    ((XmlElement)assemblyNode).SetAttribute("Name", assembly.FullName);                                                            
                    
                    Type[] assemblyTypes = assembly.GetTypes();
                    foreach (Type type in assemblyTypes){
                        if (type.Namespace != null){
                            this.AddNamespaceObject(assemblyNode, type.Namespace, type);
                        }
                    }                                    
                    this.IntillisenseDocument.DocumentElement.AppendChild(assemblyNode);
                }
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="assemblyRef"></param>                
        public void AddAssembly(string assemblyRef)
        {
            Assembly assembly = Assembly.Load(assemblyRef);
            this.AddAssembly(assembly);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="assemblyString"></param>
        public void RemoveAssembly(string assemblyString)
        {
            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="assemblyString"></param>
        public void AddAssemblyAsync(string assemblyString)
        {
            
        }

        /// <summary>
        /// 
        /// </summary>
        public List<string> GetAssemblyList()
        {
            List<string> assemblyList = new List<string>();
            foreach (XmlNode assemblyNode in this.IntillisenseDocument.SelectNodes("//Assembly")){
                assemblyList.Add(assemblyNode.Attributes["Name"].Value);
            }
            return assemblyList;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="matchFilter"></param>
        /// <returns></returns>
        public XmlNodeList MatchNodes(string matchFilter)
        {
            return this.IntillisenseDocument.DocumentElement.SelectNodes("//*/contains(@Name,'" + matchFilter + "')");
        }

        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            this.IntillisenseDocument = null;   
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="assemblyNode"></param>
        /// <param name="objNamespace"></param>
        /// <param name="objType"></param>
        private void AddNamespaceObject(XmlNode assemblyNode, string objNamespace, Type objType)
        {
            string ns = string.Empty;            
            string[] nsParts = objNamespace.Split('.');

            XmlNode currentNode = assemblyNode;
            
            for (int i = 0; i < nsParts.Length; i++){
                if (ns.Length > 0){
                    ns += ".";
                }
                ns += nsParts[i];

                XmlNode findNode = currentNode.SelectSingleNode("//Namespace[@Name='" + ns + "']");
                
                if (findNode != null){
                    currentNode = findNode;
                }
                else{
                    XmlNode namespaceNode = this.IntillisenseDocument.CreateNode(XmlNodeType.Element, "Namespace", null);
                    ((XmlElement)namespaceNode).SetAttribute("Name", ns);
                    currentNode.AppendChild(namespaceNode);
                    currentNode = namespaceNode;
                }                               
            }

            XmlNode membersNode = this.IntillisenseDocument.CreateNode(XmlNodeType.Element, "Type", null);
            ((XmlElement)membersNode).SetAttribute("Name", objType.Name);

            foreach (MethodInfo mi in objType.GetMethods(BindingFlags.Public)){
                XmlNode methodNode = this.IntillisenseDocument.CreateNode(XmlNodeType.Element, "Method", null);
                ((XmlElement)methodNode).SetAttribute("Name", mi.Name);
                membersNode.AppendChild(methodNode);
            }            
            currentNode.AppendChild(membersNode);
        }
    }
}
